/*****************************************************************************
*    Ali Corp. All Rights Reserved. 2002 Copyright (C)
*
*    File:    bootloader.c
*
*    Description:    This file contains all globe micros and functions declare
*                    of Flash boot loader.
*    History:
*           Date            Athor        Version          Reason
*       ============    =============   =========   =================
*   1.  Oct.28.2003     Justin Wu       Ver 0.1    Create file.
 *  2.  2006.5.16       Justin Wu       Ver 0.2    STO chunk support & clean up.
*****************************************************************************/
#include "bootloader.h"
#ifdef BL_VMX_AS //aaron
#include <api/libcas/vmx/bc_types.h>
#endif


UINT8  g_crc_bad = 0;
UINT32 unzip_length=0;
UINT32 see_unzip_length=0;
UINT8 g_uk_pos[16] = {0};

struct sto_device *flash_dev = NULL;
UINT8 *g_see_unziped   = NULL;

#ifdef BL_VMX_AS
static INT32 handle_chunk_data(UINT8 *data, UINT32 length);
#endif


#ifdef ENABLE_UART_UPG
INT32 update_flash_data(UINT32 offset, INT32 len, UINT8 * data)
{
	UINT8 *app_flash_buffer = NULL;
	INT32 ret=0;
	FIXED_PRINTF("%s-offset(0x%x),len(0x%x),data(0x %2x %2x %2x %2x)\n",__FUNCTION__,offset,len,data[0],data[1],data[2],data[3]);	
	struct sto_device *sto_dev = (struct sto_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_STO);

	app_flash_buffer = MALLOC(64*1024);
	if(app_flash_buffer==NULL)
	{
		FIXED_PRINTF("update_flash_data: allocate buffer failed\n");
		return -1;
	}

	sto_io_control(sto_dev, STO_DRIVER_SECTOR_BUFFER, (UINT32)app_flash_buffer);
	sto_io_control(sto_dev, STO_DRIVER_SET_FLAG, STO_FLAG_AUTO_ERASE|STO_FLAG_SAVE_REST);
	ret=sto_put_data(sto_dev, (UINT32)offset, data, (INT32)len);

	FREE(app_flash_buffer);
	app_flash_buffer = NULL;
	sto_io_control(sto_dev, STO_DRIVER_SECTOR_BUFFER, 0);
	sto_io_control(sto_dev, STO_DRIVER_SET_FLAG, 0);

	return ret;
}
#endif

//Apply vmx std boot flow
#ifdef BL_VMX_STD
int verifySignature_std( UINT8* src, UINT8* dest, CHUNK_HEADER hdr,UINT8 *errorCode)
{
	FIXED_PRINTF("Verify chunk (0x%x)!\n",hdr.id);
	int ret=0,val=0;
	*errorCode=1;
	UINT8 kid=0;
	if(NO_CRC!=hdr.crc) 
	{
		kid=1;	//common key
		FIXED_PRINTF("Use Common Key!\n");
	}
	else
	{
		FIXED_PRINTF("Use AES Key!\n");
	}	
	ret=aes_cbc_std(0,kid,src,dest);
	FIXED_PRINTF("%s-run aes_cbc_std , ret: %d.\n",__FUNCTION__,ret);	
    get_sys_pub_key(PK_FROM_OTP,NULL,0);
    val=Test_RSA_Ram((UINT32)dest+CHUNK_HEADER_SIZE,hdr.len-CHUNK_HEADER_SIZE + CHUNK_NAME);	
	FIXED_PRINTF("%s-run Test_RSA_Ram , ret: %d.\n",__FUNCTION__,val);	
	if(0!=val)		//case of no valid signature	
	{		
		if(-2==val)	//Special case during MP (No RSA Key)	
		{
			FIXED_PRINTF("Skip RSA Check, due to Zero RSA Key!\n");	
			ret=1;
			*errorCode=0;
		}
		else
		{
			FIXED_PRINTF("No valid signature!\n");
			if(1==kid)
			{
				*errorCode = 3;	//Need hang up, since check fail with common key
			}
			else
			{
				*errorCode = 1;
			}
			return ret;
		}		
	}	
	//valid signature
	if(0==kid)
	{
		ret=1;
		*errorCode=0;
		FIXED_PRINTF("Success to check chunk.\n");
	}
	else if(1==kid)
	{
		MEMSET(src,0xFF,hdr.offset);
		val=aes_cbc_std(1,0,dest,src);
		if(0==val)
		{
			*errorCode=2;	//case of need to store reEnc code to flash		
			FIXED_PRINTF("Need to store data to flash!\n");
		}
		else if(-2==val)	//special case during MP Flow (No AES Key)
		{
			*errorCode=0;
			ret=1;
			FIXED_PRINTF("Spcial case during MP !\n");
		}
	}		
	return ret;
}
static INT32 handle_chunk_data_std(UINT8 *data, UINT32 length)
{
	int ret;
	UINT8* tmp_buffer;
	UINT32 len;
	UINT8 errorCode;
	UINT32 chunk_id, block_len;
	unsigned int header_crc=0;
	INT32 ret_val = 0;
	UINT32 pre_chunk_id =0;
	UINT32 pre_chunk_offset=0;


	CHUNK_HEADER tchunk_hdr;
	tchunk_hdr.id = fetch_long(data + CHUNK_ID);
	tchunk_hdr.len= fetch_long(data + CHUNK_LENGTH);
	tchunk_hdr.offset=fetch_long(data + CHUNK_OFFSET);
	tchunk_hdr.crc=fetch_long(data + CHUNK_CRC);    
	
	chunk_id=tchunk_hdr.id;
	block_len=tchunk_hdr.offset;	
	tmp_buffer = (UINT8*)(0xa0000000|(0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)));
	ret = verifySignature_std(data, tmp_buffer, tchunk_hdr,&errorCode);
	FIXED_PRINTF("%s:chkret(%d),errorcode(%d)\n",__FILE__,ret,errorCode);
	if(ret == 1)
	{
		//FIXED_PRINTF("code verify ok, but should not enter this case in force donwload...\n");
	}
	else
	{
		if(errorCode==3)
		{
			pan_display(panel_dev, "ERRC", 4);
			do{} while(1);
		}
		else if(errorCode==2)
		{
			UINT32 block_addr = 0;

			switch(chunk_id)
			{
				case CHUNKID_MAINCODE:
					block_addr = sto_chunk_goto(&chunk_id, CHUNKID_MAINCODE_MASK, 1);
					if(ERR_FAILUE == block_addr)
					{
						pre_chunk_id = CHUNKID_STBINFO;
						pre_chunk_offset=0;
						
						block_addr = sto_chunk_goto(&pre_chunk_id, CHUNKID_STBINFO_MASK, 1);
						pre_chunk_offset = sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);

						if(ERR_FAILUE == block_addr)
							ret_val = -1;
						else
							block_addr += pre_chunk_offset;
					}
					break;

				case CHUNKID_SEECODE:
					block_addr = sto_chunk_goto(&chunk_id, CHUNKID_SEECODE_MASK, 1);
					if(ERR_FAILUE == block_addr)
					{
						pre_chunk_id = CHUNKID_MAINCODE;
						pre_chunk_offset=0;
						
						block_addr = sto_chunk_goto(&pre_chunk_id, CHUNKID_MAINCODE_MASK, 1);
						pre_chunk_offset = sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);

						if(ERR_FAILUE == block_addr)
							ret_val = -1;
						else
							block_addr += pre_chunk_offset;
					}
					break;

				default:
					//not supported chunk id in force download
					ret_val = -1;
					break;
			}
			if( ret_val == 0 )
			{
				pan_display(panel_dev, "BURN", 4);
				update_flash_data(block_addr, block_len, data);
			}
			else
			{
				pan_display(panel_dev, "FAIL", 4);
				do{} while(1);
			}
		}
		else if(errorCode==1)
		{
			ret_val = -1;
		}
	}

	FREE(tmp_buffer);

	return ret_val;
}
#endif


#ifdef ENABLE_UART_UPG
#ifdef US_TICKS
#undef US_TICKS
#endif
#ifdef WAIT_300MS
#undef WAIT_300MS
#endif
#define US_TICKS       (sys_ic_get_cpu_clock()*1000000 / 2000000)
#define WAIT_100MS          (100000 * US_TICKS)
#define WAIT_300MS      	(300000 * US_TICKS)
#define WAIT_500MS		    (500000 * US_TICKS)
#define WAIT_5MS        	(5000 * US_TICKS)
#define MS_TICKS		(1000*US_TICKS)
static void nosdelay(UINT32 timeout)
{
	UINT32 stamp;
	stamp = read_tsc();
	while(read_tsc() - stamp < timeout*MS_TICKS);
}

BOOL check_sw_upgrade()
{
    BOOL ret = FALSE;
    UINT32 dwtick;
    UINT8  ch;
	
    dwtick = read_tsc();
    do
    {
    	if (sci_read_tm(SCI_FOR_RS232, &ch, 5) == SUCCESS && ch == 'c')
        {
        	ret=TRUE;
        	break;
    	}	
        else
        	ch = 0;
    }
    while (read_tsc() - dwtick < WAIT_300MS);
    
    return ret;
}
void serial_upgrade(void)
{
	hw_watchdog_stop();
	#ifdef BL_VMX_STD
	lib_ash_set_data_callback(handle_chunk_data_std); 
	#elif defined BL_VMX_AS
	lib_ash_set_data_callback(handle_chunk_data);
	#endif
	lib_ash_shell();
}
#endif

#if defined(BL_VMX_STD) || defined(BL_VMX_AS)
UINT8 *expand_ram ( UINT32 addr, UINT32 len, int unzip ( UINT8 *, UINT8 *, UINT8 * ), UINT32 type )
{
        UINT8 *buffer;
        UINT8 *codeentry;
        UINT32 i = 0;

        if ( type == MAIN_CODE || type == OTA_CPU_CODE )
                codeentry = ( UINT8 * ) MAIN_ENTRY;
        else if ( type == SEE_CODE || type == OTA_SEE_CODE )
                codeentry = g_see_unziped;

        buffer = MALLOC ( BUFFER_SIZE );
        if ( buffer == NULL )
        {
                FIXED_PRINTF ( "Boot loader: No decompress buffer!\n" );
                return 0;
        }

        if ( unzip ( ( void * ) ( addr ), codeentry, buffer ) != 0 )
        {
                codeentry = 0;
                FIXED_PRINTF ( "Boot loader: error to decompress chunk(type %d)!\n", type );
        }
        else
        {
               // unzip_length = * ( UINT32* ) buffer;
              if ( type == MAIN_CODE || type == OTA_CPU_CODE )
                unzip_length = * ( UINT32* ) buffer;
              else if ( type == SEE_CODE || type == OTA_SEE_CODE )
                see_unzip_length = * ( UINT32* ) buffer;
        }

        FREE ( buffer );
        return codeentry;
}
#else
UINT8 *expand_ram ( UINT32 addr, UINT32 len, int unzip ( UINT8 *, UINT8 *, UINT8 * ), UINT32 type )
{
        UINT8 *buffer;
        UINT8 *codeentry;
        UINT32 i = 0;

        if ( type == MAIN_CODE || type == OTA_CPU_CODE )
                codeentry = ( UINT8 * ) MAIN_ENTRY;
        else if ( type == SEE_CODE || type == OTA_SEE_CODE )
                codeentry = g_see_unziped;

        buffer = MALLOC ( BUFFER_SIZE );
        if ( buffer == NULL )
        {
                FIXED_PRINTF ( "Boot loader: No decompress buffer!\n" );
                return 0;
        }

        if ( unzip ( ( void * ) ( addr ), codeentry, buffer ) != 0 )
        {
                codeentry = 0;
                FIXED_PRINTF ( "Boot loader: error to decompress chunk(type %d)!\n", type );
        }
        else
        {
                unzip_length = * ( UINT32* ) buffer;
        }
        osal_cache_flush(codeentry,unzip_length);
        FREE ( buffer );
        return codeentry;
}
#endif
void set_os_config ( UINT16 task_num, UINT16 sema_num, UINT16 flg_num, UINT16 mbf_num, UINT16 mutex_num )
{
        struct os_config *ptr = ( struct os_config * ) 0xa00001e0;

        ptr->task_num = task_num;
        ptr->sema_num = sema_num;
        ptr->flg_num = flg_num;
        ptr->mbf_num = mbf_num;
        ptr->mutex_num = mutex_num;
        ptr->parity_value = ptr->task_num + ptr->sema_num + ptr->flg_num + ptr->mbf_num + ptr->mutex_num;
}

#ifndef _LINUX_BOOTLOADER_
void bl_enter_ota_loader(UINT8 *ota_decrypted, UINT32 ota_len)
{
    UINT32 chunk_head_length = 128;
    UINT32 ota_cpu_len = 0;
    UINT32 ota_see_addr = 0;
    UINT32 ota_see_len = 0;
    UINT32 ziped_len = 0;
    UINT8 *p=NULL;
    UINT8 *main_entry=NULL, *see_entry=NULL;
    UINT8 *see_unziped=NULL;

    FIXED_PRINTF ( "\nEnter OTA loader by sys_sw_wrong \n");
    p = ota_decrypted + ( ota_len - 0x200 );
    ziped_len = ( p[3] | ( p[2] << 8 ) | ( p[1] << 16 ) | ( p[0] << 24 ) );
    expand_ram ( ota_decrypted, ziped_len, un7zip, OTA_CPU_CODE );
    ota_decrypted = ( UINT32 ) MALLOC ( unzip_length + 0xf );
    ota_decrypted = ( UINT8 * ) ( ( 0xFFFFFFF8 & ( UINT32 ) (ota_decrypted )) );
    FIXED_PRINTF ( "ota_decrypted: 0x%08x\n", ota_decrypted );
    memcpy ( ota_decrypted, ( UINT8 * ) MAIN_ENTRY, unzip_length );

    p = ota_decrypted + 0x4;
    ota_cpu_len = ( p[3] | ( p[2] << 8 ) | ( p[1] << 16 ) | ( p[0] << 24 ) );
    p = ota_decrypted + 0x8;
    ota_see_addr = ( p[3] | ( p[2] << 8 ) | ( p[1] << 16 ) | ( p[0] << 24 ) );
    p = ota_decrypted + ota_see_addr + 0x4;
    ota_see_len = ( p[3] | ( p[2] << 8 ) | ( p[1] << 16 ) | ( p[0] << 24 ) );
    FIXED_PRINTF ( "\n cpu:0x%x,see:0x%x,0x%x\n", ota_cpu_len, ota_see_len, ota_see_addr );

    //excute see ota code
    FIXED_PRINTF ( "expand ota see code, ziped len: %d\n", ota_see_len );
    //for decompress use.
    g_see_unziped = ( UINT32 ) MALLOC ( ota_see_len * 4 + 0xf );
	//  osal_cache_invalidate ( g_see_unziped, ota_see_len * 4 + 0xf );
	g_see_unziped = ( UINT8* ) ( ( 0xFFFFFFF8 & ( UINT32 ) g_see_unziped ) );    
    
    see_entry = expand_ram ( (ota_decrypted + ota_see_addr + chunk_head_length), ota_see_len, un7zip, OTA_SEE_CODE );
    if ( see_entry )
    {
	        osal_cache_flush ( see_entry, see_unzip_length );
	        FIXED_PRINTF ( "success, see_unzip_length len: %d,g_see_unziped:0x%08x\n", see_unzip_length,g_see_unziped );
	        self_boot ( SEE_ENTRY, see_entry, see_unzip_length, 0 );
    }
    //excute cpu ota code
    FIXED_PRINTF ( "expand ota cpu code, ziped len: %d\n", ota_cpu_len );
    main_entry = expand_ram ( ota_decrypted + chunk_head_length, ota_cpu_len, un7zip, OTA_CPU_CODE );
    if ( main_entry )
    {
            set_os_config ( 128, 64, 64, 64, 64 );
            FIXED_PRINTF ( "success, unziped len: %d\n", unzip_length );
            hw_watchdog_stop();
	        osal_cache_flush ( main_entry, unzip_length );
            exec ( main_entry );
    }
    return RET_FAILURE;
}

UINT8* bl_unzip_sw(UINT8 *decrypted, UINT32 mc_len,UINT32 type)
{
    UINT32 ziped_len = 0;
    UINT8 *p=NULL;   
    UINT8 *entry=NULL;

    p = decrypted + ( mc_len - 0x200 );
    ziped_len = p[3] | ( p[2] << 8 ) | ( p[1] << 16 ) | ( p[0] << 24 );
    FIXED_PRINTF ( "expand code, ziped len: 0x%x\n", ziped_len );
#ifdef LZO_COMPRESS_ENABLE
    entry = expand_ram ( decrypted, ziped_len, unlzo, type );
#else
    entry = expand_ram ( decrypted, ziped_len, un7zip, type );
#endif    
    return entry; 
}
#endif

#if defined(BL_VMX_STD) || defined(BL_VMX_AS)
void bl_enter_sys_sw(UINT8 *see_decrypted, UINT32 see_mc_len,UINT8 *main_decrypted, UINT32 cpu_mc_len)
{
	UINT32 ziped_len = 0;
	UINT8 *p=NULL;   
	UINT8 *main_entry=NULL, *see_entry=NULL;
	p = see_decrypted + ( see_mc_len - 0x200 );
	ziped_len = p[3] | ( p[2] << 8 ) | ( p[1] << 16 ) | ( p[0] << 24 );
	//enter Maincode
	FIXED_PRINTF ( "expand see main code, ziped len: %d\n", ziped_len );
#ifdef LZO_COMPRESS_ENABLE
	see_entry = expand_ram ( see_decrypted, ziped_len, unlzo, SEE_CODE );
#else
	see_entry = expand_ram ( see_decrypted, ziped_len, un7zip, SEE_CODE );
#endif
	p = main_decrypted + ( cpu_mc_len - 0x200 );
	ziped_len = p[3] | ( p[2] << 8 ) | ( p[1] << 16 ) | ( p[0] << 24 );
	FIXED_PRINTF ( "expand cpu main code, ziped len: %d\n", ziped_len );
#ifdef LZO_COMPRESS_ENABLE
	main_entry = expand_ram ( main_decrypted, ziped_len, unlzo, MAIN_CODE );
#else
	main_entry = expand_ram ( main_decrypted, ziped_len, un7zip, MAIN_CODE );
#endif

	if ( see_entry )
	{
		FIXED_PRINTF ( "success, unziped len: %d,see entry = 0x%x,\n", see_unzip_length, see_entry );
		osal_cache_flush ( see_entry, see_unzip_length );
		self_boot ( SEE_ENTRY, see_entry, see_unzip_length, 0 );
	}
	if ( main_entry != 0 )
	{
		FIXED_PRINTF ( "success, unziped len: %d.\n", unzip_length );
		set_os_config ( 128, 64, 64, 64, 64 );
		FIXED_PRINTF ( "jump to fw entry=%x\n", main_entry );
		hw_watchdog_stop();
		osal_cache_flush ( main_entry, unzip_length );
		exec ( main_entry );
	}
}
#else
RET_CODE bl_enter_sys_sw(UINT8 *see_entry, UINT32 see_mc_len,UINT8 *main_entry, UINT32 cpu_mc_len)
{
	if ( main_entry != 0 )
    {
        FIXED_PRINTF ( "success, cpu len: %d.\n", cpu_mc_len );
        set_os_config ( 128, 64, 64, 64, 64 );
        FIXED_PRINTF ( "jump to fw entry=%x\n", main_entry );
        hw_watchdog_stop();
        osal_cache_flush ( main_entry, cpu_mc_len );
        exec ( main_entry );
    }else
		return !RET_SUCCESS; 	
}
#endif
#ifdef _HW_SECURITY_ENABLE_
void set_uboot_config(UINT32 uboot_entry)
{    
	fetch_sys_kernel_pub_key(uboot_entry - CFG_KEY_LEN);
}

RET_CODE bl_prepare_key(struct UKEY_INFO *ukey)
{
    static UINT32 chid = 0;
    CHUNK_HEADER chunk_hdr;
    UINT32 offset = 0;
    RET_CODE ret = RET_FAILURE;

    if (NULL == ukey)
    {
        return ret;
    }

    chid = CHUNKID_MEM_CFG;
	/*bl_uk*/
    if ( ( is_bl_encrypted_enable() ) && ( sto_get_chunk_header ( CHUNKID_MEM_CFG, &chunk_hdr ) != 0 ) )
    {
        offset = sto_chunk_goto ( &chid, CHUNKID_MEM_CFG_MASK, 1 );
        if ( ERR_FAILUE != offset )
        {
            if (is_key_encrypted(offset, BL_FLASH_KEY)==FALSE)
            {
                ret = encrypt_bl_universal_key ( CHUNKID_MEM_CFG&CHUNKID_MEM_CFG_MASK);
                if(ret!=RET_SUCCESS)
                    return ret;

                if (g_boot_type == 1)
                {
                    api_store_key_to_nandflash(CHUNKID_MEM_CFG);
                }
            }
        }
    }

    chid = CHUNKID_KEY;
    if ( sto_get_chunk_header ( CHUNKID_KEY, &chunk_hdr ) != 0 )
    {
        offset = sto_chunk_goto ( &chid, CHUNKID_KEY_MASK, 1 );
        if ( ERR_FAILUE != offset )
        {

			if ( is_key_encrypted(offset, FIRST_FLASH_KEY)==FALSE )
            {
                ret = encrypt_universal_key ( (CHUNKID_KEY&CHUNKID_KEY_MASK)|FIRST_FLASH_KEY);
                if(ret!=RET_SUCCESS)
                    return ret;

                if (g_boot_type == 1)
                {
                    api_store_key_to_nandflash(CHUNKID_KEY);
                }
            }
	#ifdef _LINUX_BOOTLOADER_
			/*encrypt 2nd uk*/
			if ( is_key_encrypted(offset, SECOND_FLASH_KEY)==FALSE )
            {
                ret = encrypt_universal_key ((CHUNKID_KEY&CHUNKID_KEY_MASK)|SECOND_FLASH_KEY);
                if(ret!=RET_SUCCESS)
                    return ret;

                if (g_boot_type == 1)
                {
                    api_store_key_to_nandflash(CHUNKID_KEY);
                }
            }
    #endif			
            ret = decrypt_universal_key(g_uk_pos, (CHUNKID_KEY&CHUNKID_KEY_MASK|FIRST_FLASH_KEY));
            if(ret!=RET_SUCCESS)
                  return ret;
        }
    }

    if (sys_ic_get_KL_key_mode(2) == 3)
    {
        ukey->sram_ukey->uk = g_uk_pos;
        ukey->sram_ukey->len = ALIASIX_SW_UK_LEN;
        ukey->ukey_type = KEY_FROM_SRAM;
    }
    else
    {
        ukey->ce_ukey->pos = g_uk_pos[0];
        ukey->ukey_type = KEY_FROM_CRYPTO;
    }

    return (ret = RET_SUCCESS);
}

RET_CODE flush_ukey(struct UKEY_INFO *ukey)
{
    if (NULL == ukey)
    {
        return RET_FAILURE;
    }

    if (KEY_FROM_CRYPTO == ukey->ukey_type)
    {
        osal_cache_flush(ukey->ce_ukey, sizeof(struct CE_UKEY));
        ukey->ce_ukey = (UINT32)ukey->ce_ukey | 0xA0000000;
    }
    else if (KEY_FROM_SRAM == ukey->ukey_type)
    {
        osal_cache_flush(ukey->sram_ukey->uk, 16);
        ukey->sram_ukey->uk = (UINT32)ukey->sram_ukey->uk | 0xA0000000;
        osal_cache_flush(ukey->sram_ukey, sizeof(struct SRAM_UKEY));
        ukey->sram_ukey = (UINT32)ukey->sram_ukey | 0xA0000000;
    }
    else
    {
        return RET_FAILURE;
    }

    osal_cache_flush(ukey, sizeof(struct UKEY_INFO));

    return RET_SUCCESS;
}
#endif

RET_CODE bl_get_chunk_len(UINT32 id, UINT32 *addr, UINT32 *len)
{
	INT32 ret;
	CHUNK_HEADER chunk_hdr;

	ret = sto_get_chunk_header(id, &chunk_hdr);
	if (ret == 0)
		return RET_FAILURE;

	*addr = (UINT32)sto_chunk_goto(&id, 0xFFFFFFFF, 1) + CHUNK_HEADER_SIZE;
	*len = chunk_hdr.len - CHUNK_HEADER_SIZE + CHUNK_NAME;

	return RET_SUCCESS;
}

void bl_init_flash()
{
    UINT8  flash_speed;
    UINT8 * aboot_buf;

    if (g_boot_type != 1)
    {
        //init flash device
    	*((unsigned long *)0xb802e098) |=  0xc2000000;  //for flash can read 4M

#ifndef ENABLE_SERIAL_FLASH
#ifndef _LINUX_BOOTLOADER_	
        flash_info_pl_init(); 	
#endif
    	sto_local_flash_attach(NULL);
#else
#ifndef _LINUX_BOOTLOADER_	
        flash_info_sl_init();	
#endif
    	sto_local_sflash_attach(NULL);
#endif
    }
    else
    { // Nand boot
#if (defined _LINUX_BOOTLOADER_) && (!defined _NAND_WITHOUT_PMI_)
        if (bl_init_nand() != 0)
        {
            FIXED_PRINTF("Nand Flash init fail.\n");
            hw_watchdog_reboot();
        }
    
		//Copy bootloader code from nandflash to ram address 0xA8000000
        FIXED_PRINTF("Load bootloader:\n");
        aboot_buf = (UINT8*)RAM_BASE_ADDR;
        // the loader_len of M3503 is just only the bootloader real size
        if (api_load_data_from_nandflash(g_nand_info.loader_start, 0x200000, aboot_buf) != 0)
        {
            FIXED_PRINTF("Load aboot fail\n");
            hw_watchdog_reboot();
        }

        sto_local_sflash_attach(NULL);
#else
        FIXED_PRINTF("%s, Unsupported boot type.\n", __FUNCTION__);
        hw_watchdog_reboot();
#endif
    }

    flash_dev = (struct sto_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_STO);
	if (flash_dev == NULL)
	{
		FIXED_PRINTF("Can't find FLASH device!\n");
	}
	sto_open(flash_dev);
    sto_chunk_init(0, flash_dev->totol_size);
	FIXED_PRINTF("flash init OK,flash size is:%x!\n",flash_info.flash_size);
}

UINT8 check_program(void)
{
    UINT32 i;
    UINT8 *buffer;
    UINT32 data_id;
    UINT32 data_len;
    UINT32 data_off;
    UINT32 data_crc;
    UINT32 crc;
    UINT32 pointer = 0;

    do
    {
        data_id  = sto_fetch_long((UINT32)pointer + CHUNK_ID);
        data_len = sto_fetch_long((UINT32)pointer + CHUNK_LENGTH);
        data_off = sto_fetch_long((UINT32)pointer + CHUNK_OFFSET);
        data_crc = sto_fetch_long((UINT32)pointer + CHUNK_CRC);

        if (pointer != 0)
        {
            if (((data_id >> 16) & 0xFF) + ((data_id >> 24) & 0xFF) != 0xFF)
                return 1;
        }
        if (data_off > flash_dev->totol_size)
        {
            FIXED_PRINTF("@pointer[%08X] id[%08X] data_off[%08X] > flash size\n", \
                  pointer, data_id, data_off);
            return 2;
        }
        if ((data_off != 0) && (data_len > data_off))
        {
            FIXED_PRINTF("@pointer[%08X] id[%08X] data_len[%08X] > data_off[%08X]\n", \
                  pointer, data_id, data_len, data_off);
            return 3;
        }
	    if (data_crc != NO_CRC)
        {
			if ((buffer = (UINT8 *)MALLOC(data_len)) == NULL)
                ASSERT(0);
            sto_get_data(flash_dev, buffer, pointer + 16, data_len);
            crc = MG_Table_Driven_CRC(0xFFFFFFFF, buffer, data_len);
            FREE(buffer);
            if (data_crc != crc)
            {
                FIXED_PRINTF("@pointer[%08X] id[%08X] crc[%08X] != data_crc[%08X]\n", \
                    pointer, data_id, crc, data_crc);
                return 4;
            }
        }
        pointer += data_off;
        if (pointer + CHUNK_HEADER_SIZE > flash_dev->totol_size)
        {
            FIXED_PRINTF("@pointer[%08X] no space for header\n", pointer);
            return 5;
        }

    }while (data_off);

    return 0;
}

#if(defined(ENABLE_4M_FLASH))
UINT32 g_ota_offset = 0;
UINT8 find_second_loader ( void )
{
        UINT32 pointer = 0;
        UINT32 data_id = 0;
        UINT32 data_len = 0;
        UINT32 data_off = 0;
        UINT32 data_crc = 0;
        UINT8 *buffer = NULL;
        UINT32 crc = 0;
        UINT8 bSecondOta = 0;

        FIXED_PRINTF ( "start find_second_loader()!\n" );
        while ( pointer < flash_dev->totol_size )
        {
                data_id = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_ID );
                data_len = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_LENGTH );
                data_off = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_OFFSET );
                data_crc = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_CRC );

                //FIXED_PRINTF("@pointer[%08X] id[%08X] len[%08X] off[%08X] crc[%08X]\n", \
                //                      pointer, data_id, data_len, data_off, data_crc);


			#ifdef BL_VMX_STD
				if ((0!=data_len)&&(data_len<data_off)&&(CHUNKID_SECLOADER==data_id))
				{				
                	g_ota_offset = pointer;
	                bSecondOta = 1;				
			        FIXED_PRINTF ( "Find OTA LOADER at 0x%x!\n",g_ota_offset);
					break;
				}				
			#else
                if ( ( 0 != data_len ) && ( data_len < data_off ) && ( data_crc != NO_CRC ) \
                     && ( ( CHUNKID_SECLOADER == data_id ) ) )
                {
                        buffer = ( UINT8 * ) MALLOC ( data_len );
                        if ( NULL != buffer )
                        {
                                sto_get_data ( flash_dev, buffer, pointer + 16, data_len );
                                crc = MG_Table_Driven_CRC ( 0xFFFFFFFF, buffer, data_len );
                                FREE ( buffer );
                                if ( data_crc == crc )
                                {
                                        g_ota_offset = pointer;
                                        bSecondOta = 1;
                                        FIXED_PRINTF ( "Find OTA LOADER!\n" );
                                        break;
                                }
                                else
                                {
                                        FIXED_PRINTF ( "OTA CRC Check not pass!\n" );
                                }
                        }
                }
			#endif
                pointer += C_SECTOR_SIZE;
        }

        // verify the second LOADER_OPK_SIZE

        if ( bSecondOta )
        {
                pointer = g_ota_offset;
                do
                {

                        data_id = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_ID );
                        data_len = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_LENGTH );
                        data_off = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_OFFSET );
                        data_crc = sto_fetch_long ( ( UINT32 ) pointer + CHUNK_CRC );

                        if ( !data_off )
                        {
                                break;
                        }
                        if ( ( (( data_id >> 16 ) & 0xFF) + (( data_id >> 24 ) & 0xFF) ) != 0xFF )
                        {
                                bSecondOta = 0;
                                break;
                        }

                        if ( data_off > flash_dev->totol_size )
                        {
                                //FIXED_PRINTF("@pointer[%08X] id[%08X] data_off[%08X] > flash size\n", \
                                //      pointer, data_id, data_off);
                                bSecondOta = 0;
                                break;
                        }

                        if ( NO_CRC != data_crc )
                        {
                                if ( NULL == ( buffer = ( UINT8 * ) MALLOC ( data_len ) ) )
                                {
                                        FIXED_PRINTF ( "MALLOC fail!\n" );
                                        ASSERT ( 0 );
                                }

                                sto_get_data ( flash_dev, buffer, pointer + 16, data_len );
                                crc = MG_Table_Driven_CRC ( 0xFFFFFFFF, buffer, data_len );
                                FREE ( buffer );
                                FIXED_PRINTF ( "Read OTALOADER CRC!\n" );

                                if ( data_crc != crc )
                                {
                                        FIXED_PRINTF ( "@pointer[%08X] id[%08X] crc[%08X] != data_crc[%08X]\n", \
                                                       pointer, data_id, crc, data_crc );
                                        bSecondOta = 0;
                                        break;
                                }

                        }
                        pointer += data_off;

                        if ( pointer + CHUNK_HEADER_SIZE > flash_dev->totol_size )
                        {
                                FIXED_PRINTF ( "@pointer[%08X] no space for header\n", pointer );
                                bSecondOta = 0;
                                break;
                        }
                }
                while ( data_off );
        }

        if ( 0 == bSecondOta )
        {
                FIXED_PRINTF ( "Second Loader not exist!\n" );
                g_ota_offset = 0;
        }
        FIXED_PRINTF ( "LEAVE find_second_loader\n" );
        return bSecondOta;
}
#endif
#ifdef _HW_SECURITY_ENABLE_
RET_CODE bl_prepare_otaloader(UINT8 *ota_decrypted,UINT8 key_pos, UINT32 block_addr, UINT32 block_len, UINT8 *len)
{
    RET_CODE ret = RET_FAILURE;
    UINT32 ota_len = 0;
    aes_cbc_decrypt_flash_data ( key_pos, block_addr, block_len - CHUNK_HEADER_SIZE + CHUNK_NAME, ota_decrypted );
    FIXED_PRINTF ( "Verify OTA data!\n" );
    ota_len = block_len - CHUNK_HEADER_SIZE + CHUNK_NAME;
    *len = ota_len;
    FIXED_PRINTF ( "ota_len: %08X\n", ota_len );
    if ( 0 != Test_RSA_Ram ( ( UINT32 ) ota_decrypted, ota_len ) )
    {
            FIXED_PRINTF ( "OTA signature fail!\n" );
            pan_display ( panel_dev, "ERR", 4 );
            return RET_FAILURE;
    }
    else
    {
            FIXED_PRINTF ( "OTA signature Pass!\n" );
            return RET_SUCCESS;
    }
}

#endif
#ifdef BL_VMX_AS

#define FAKE_VERITY_SIGNATURE //Aaron Test

#ifdef FAKE_VERITY_SIGNATURE
/****************************************************************************
 *  ViewRight Functions
 ****************************************************************************/
//typedef unsigned long long uint64_t;

static uint8_t magic[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };

uint32_t crc32(uint8_t *data, uint32_t len)
{
	return MG_Table_Driven_CRC(0xFFFFFFFF, data, len);
}

uint64_t getNumber( uint8_t* ptr ) 
{
	uint64_t tmpVal = 0;
	int i;
	// data in the signature is big-endian
	for( i = 0; i < 8; i++ ) 
	{
		tmpVal = (tmpVal<<8) | ptr[i];
	}
	return tmpVal;
}

uint8_t checkTheDataAndHash(uint8_t *data, uint32_t len, uint8_t* signature)
{
	// this function verifies the hash values and determines whether a
	// re-encryption is needed
	uint32_t tmpCrc = crc32( data, len );
	
	/* we only have put 32-bits into the signature therefore we type-cast and correct the offset by 4 */
	if( ( (uint32_t)getNumber( signature + 48 - 4 ) ) == tmpCrc )
	{
	#if 1	// here we "decrypt" by using xor
		int j;
		FIXED_PRINTF("checkTheDataAndHash: tmpCrc=0x%x match, decrypt the source\n",tmpCrc);
		for( j = 0; j < len; j++ )
			data[j] ^= 0xff;
	#endif
		return 0;
	}
	else 
	{
		// here we can already "re-encrypt" by using xor
		int i;
		for( i = 0; i < len; i++ )
			data[i] ^= 0xff;
		tmpCrc = crc32( data, len );
		/* we only have put 32-bits into the signature therefore we type-cast and
		correct the offset by 4 */
		if( ( (uint32_t)getNumber( signature + 48 - 4 ) ) != tmpCrc )
		{
			// the crc does not match therefore there it is an error
			FIXED_PRINTF("checkTheDataAndHash: crc error tmpCrc=0x%x\n",tmpCrc);
			return 2;
		}
		return 1;
	}
}

uint8_t decryptSignature( uint8_t* decSignature, uint8_t* signature ) 
{
	// here we do signature decryption
	// we only copy here as the signature is not encrypted at all
	memcpy( decSignature, signature, 256 );
	return 1;
}

int verifySignature( uint8_t* signature, uint8_t* src, uint8_t* tmp,uint32_t len, uint32_t maxLen, uint8_t mode,uint8_t *errorCode ) 
{
	uint8_t decSignature[256];

	FIXED_PRINTF("verifySignature: len=0x%x, maxLen=0x%x, mode=%d\n",len, maxLen, mode);
	FIXED_PRINTF("verifySignature: signature crc=0x%x 0x%x 0x%x 0x%x\n",signature[48],signature[49],signature[50],signature[51]);
	FIXED_PRINTF("verifySignature: signature len=0x%x 0x%x 0x%x 0x%x\n",signature[36],signature[37],signature[38],signature[39]);
	FIXED_PRINTF("verifySignature: signature maxlen=0x%x 0x%x 0x%x 0x%x\n",signature[44],signature[45],signature[46],signature[47]);
	// we decrypt the signature, which you do not have to do here
	if( decryptSignature( decSignature, signature ) ) 
	{
		if( memcmp( decSignature, magic, 8 ) ) 
		{
			// error magic value is wrong, therefore signature is bad
			FIXED_PRINTF("verifySignature: magic value wrong:0x%x 0x%x\n",decSignature[0], decSignature[1]);
			*errorCode = 1;
			return 0;
		}
		else 
		{
			int i;
			for( i = 9; i < 24; i++ ) 
			{
				if( decSignature[i] ) 
				{
					// error verifier is not 0, therefore signature is bad
					FIXED_PRINTF("verifySignature: zero value wrong:decSignature[%d]= 0x%x\n",i,decSignature[i]);
					*errorCode = 1;
					return 0;
				}
			}
			// the signature has been decrypted correctly, now we check the
			// length values
			if( ( getNumber( decSignature+32 ) == len ) &&( getNumber( decSignature+40 ) == maxLen ) ) 
			{
				// now we do some additional checks for hash-value
				// and from this check we determine whether a
				// re-encryption is needed
				uint8_t re_encryption_needed = checkTheDataAndHash( src, len, decSignature );
				FIXED_PRINTF("verifySignature: re_encryption_needed=%d\n",re_encryption_needed);
				if( re_encryption_needed == 2 ) 
				{
					// error the hash value is not correct,
					// therefore signature is bad
					*errorCode = 1;
					return 0;
				}
				// if re-encryption is needed we check, for the mode value.
				if( re_encryption_needed && ( decSignature[8] == 1 ) &&( mode == 0 ) ) 
				{
					// error re-encryption when starting from flash must not occur
					// for loaded image, therefore signature is bad
					FIXED_PRINTF("verifySignature: mode not match error\n");
					*errorCode = 1;
					return 0;
				}
				
				if( re_encryption_needed ) 
				{
					// do the re-encryption
					// we have already done it
					*errorCode = 2;
					return 0;
				}
				else 
				{
					if( mode == 1 ) 
					{
						// error there should be no re-encrypted image directly loaded
						FIXED_PRINTF("verifySignature: error mode should be 0\n");
						* errorCode = 1;
						return 0;
					}
					else 
					{
						// everything is fine
						*errorCode = 0;
						return 1;
					}
				}
			}
			else 
			{
				// error one of the length values does not match,
				// therefore signature is bad
				FIXED_PRINTF("verifySignature: len wrong: len=0x%x 0x%x 0x%x 0x%x\n",decSignature[32+4], decSignature[32+5],decSignature[32+6], decSignature[32+7]);
				FIXED_PRINTF("verifySignature: maxlen wrong: maxlen=0x%x 0x%x 0x%x 0x%x\n",decSignature[40+4], decSignature[40+5],decSignature[40+6], decSignature[40+7]);
				*errorCode = 1;
				return 0;
			}
		}
	}
	else 
	{
		// error decryption failed, therefore signature is bad
		FIXED_PRINTF("verifySignature: decryption failed\n");
		*errorCode = 1;
		return 0;
	}
}
#endif //FAKE_VERITY_SIGNATURE

static INT32 handle_chunk_data(UINT8 *data, UINT32 length)
{
	int ret;
	uint8_t* signature;
	uint8_t* tmp_buffer;
	uint8_t *source = NULL;
	uint32_t len, maxLen;
	uint8_t mode, errorCode;
	uint32_t chunk_id, block_len, code_len=0;
	unsigned int header_crc=0;
	INT32 ret_val = 0;

	chunk_id = fetch_long(data + CHUNK_ID);
	block_len = fetch_long(data + CHUNK_OFFSET);
	code_len = fetch_long(data + CHUNK_LENGTH);
	#define SIGNATURE_SIZE 256
	len = code_len - (CHUNK_HEADER_SIZE-CHUNK_NAME)-SIGNATURE_SIZE;
	source = data + CHUNK_HEADER_SIZE;
	signature = source + len;
	
	mode = 1;
	tmp_buffer = (UINT8*)(0xa0000000|(0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)));
	maxLen = len;

	ret = verifySignature(signature, source, tmp_buffer, len, maxLen, mode,&errorCode);

	if(ret == 1)
	{
		//FIXED_PRINTF("code verify ok, but should not enter this case in force donwload...\n");
	}
	else
	{
		if(errorCode==2)
		{
			UINT32 block_addr = 0;

			switch(chunk_id)
			{
				case CHUNKID_MAINCODE:
					block_addr = sto_chunk_goto(&chunk_id, CHUNKID_MAINCODE_MASK, 1);

					if(ERR_FAILUE == block_addr)
					{
						uint32_t pre_chunk_id = CHUNKID_STBINFO;
						uint32_t pre_chunk_offset;
						
						block_addr = sto_chunk_goto(&pre_chunk_id, CHUNKID_STBINFO_MASK, 1);
						pre_chunk_offset = sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);

						if(ERR_FAILUE == block_addr)
							ret_val = -1;
						else
							block_addr += pre_chunk_offset;
					}
					break;

				case CHUNKID_SEECODE:
					block_addr = sto_chunk_goto(&chunk_id, CHUNKID_SEECODE_MASK, 1);

					if(ERR_FAILUE == block_addr)
					{
						uint32_t pre_chunk_id = CHUNKID_MAINCODE;
						uint32_t pre_chunk_offset;
						
						block_addr = sto_chunk_goto(&pre_chunk_id, CHUNKID_MAINCODE_MASK, 1);
						pre_chunk_offset = sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);

						if(ERR_FAILUE == block_addr)
							ret_val = -1;
						else
							block_addr += pre_chunk_offset;
					}
					break;

				default:
					//not supported chunk id in force download
					ret_val = -1;
					break;
			}

			if( ret_val == 0 )
			{
				//calculate crc value for chunk header
				header_crc=MG_Table_Driven_CRC(0xFFFFFFFF, data+CHUNK_NAME, code_len);
				*(data+CHUNK_CRC)=(UINT8)(header_crc>>24);
				*(data+CHUNK_CRC+1)=(UINT8)(header_crc>>16);
				*(data+CHUNK_CRC+2)=(UINT8)(header_crc>>8);
				*(data+CHUNK_CRC+3)=(UINT8)(header_crc);
				pan_display(panel_dev, "BURN", 4);
				update_flash_data(block_addr, block_len, data);
			}
			else
			{
				pan_display(panel_dev, "fail", 4);
				while(1);
			}
		}
		else if(errorCode==1)
		{
			ret_val = -1;
		}
	}

	FREE(tmp_buffer);

	return ret_val;
}
#endif

void api_store_key_to_nandflash(UINT32 id)
{
#if (defined _LINUX_BOOTLOADER_) && (!defined _NAND_WITHOUT_PMI_)
    UINT32 block_addr = 0, block_len = 0, ret_len = 0, addr = 0, len = 0;
    loff_t flash_addr = 0;
    struct ali_nand_device *nand_dev;

	nand_dev = (struct ali_nand_device *)dev_get_by_name("ALI_NAND");

    if (RET_FAILURE != bl_get_chunk_len(id, &block_addr, &block_len))
    {
        FIXED_PRINTF("[%s]block_addr = 0x%x, block_len = 0x%x\n", __FUNCTION__, block_addr, block_len);
        flash_addr = g_nand_info.loader_start + (loff_t)block_addr;
        addr = RAM_BASE_ADDR + block_addr;
        len = block_len;
        if(NF_SaveLoader(nand_dev, flash_addr, (UINT8 *)addr, len))
        {
            FIXED_PRINTF("[%s, %d]ali_nand_write\n", __FUNCTION__, __LINE__);
        }
	}
    else
    {
		FIXED_PRINTF("bl_get_chunk_len Fail!\n");
    }
#else
	return ;
#endif
}

#ifdef _LINUX_BOOTLOADER_
#ifdef _NAND_WITHOUT_PMI_
//Special nand layout without PMI only support Nor boot
RET_CODE bl_get_uboot_addr(UINT32 boot_type, UINT32 *uboot_addr)
{
    if (boot_type != 0)
    {
        FIXED_PRINTF("bl_get_uboot_addr:Unsupported boot type, boot_type = 0x%x\n", boot_type);
        return RET_FAILURE;
    }
    else
    {
        *uboot_addr = CHUNKID_MAINCODE;
        return RET_SUCCESS;
    }
}

#else
int api_load_data_from_nandflash(unsigned long addr, unsigned long len, UINT8 * buf) 
{
    struct ali_nand_device *nand_dev;

    FIXED_PRINTF("load data from flash: addr = 0x%x, len = 0x%x, buf = 0x%x\n", addr, len, buf);

    if (buf == NULL)
    {
        FIXED_PRINTF("buf is NULL\n");
        return -1;
    }

	nand_dev = (struct ali_nand_device *)dev_get_by_name("ALI_NAND");
    if(NF_LoadLoader(nand_dev, addr, buf, len))
    {
        FIXED_PRINTF("Load data from nandflash fail\n");
        return -1;
    }

    return 0;
}

RET_CODE bl_load_stmach(struct state_machine_t *stmach)
{
    UINT8 *buf_tmp;
    struct ali_nand_device *nand_dev;
    UINT32 bbsize, bbchecklen;
    unsigned long offset;

    if (stmach == NULL)
    {
        FIXED_PRINTF("stmach is null\n");
        return RET_FAILURE;
    }

    buf_tmp = (UINT8 *)MALLOC(g_nand_info.blocksize);
    if (buf_tmp == NULL)
    {
        FIXED_PRINTF("Malloc failed!\n");
        return RET_FAILURE;
    }

    nand_dev = (struct ali_nand_device *)dev_get_by_name("ALI_NAND");
    if (nand_dev == NULL)
    {
        FIXED_PRINTF("nand_dev is null\n");
        FREE(buf_tmp);
        return RET_FAILURE;
    }

    bbchecklen = 3*g_nand_info.blocksize;
    check_skip_len(nand_dev, (loff_t)(g_nand_info.stmach_addr - bbchecklen), bbchecklen, &bbsize);
    offset = g_nand_info.stmach_addr + bbsize;

    FIXED_PRINTF("Load stmach: offset = 0x%x\n", offset);
    if(NF_LoadLoader(nand_dev, (loff_t)offset, buf_tmp, g_nand_info.blocksize))
    {
        FIXED_PRINTF("Load data from nandflash fail\n");
        FREE(buf_tmp);
        return RET_FAILURE;
    }

    memcpy((UINT8 *)stmach, buf_tmp, sizeof(struct state_machine_t));

    FIXED_PRINTF("State Machine:\n");
    FIXED_PRINTF("b_boot_status = 0x%x;\n", stmach->b_boot_status);
    FIXED_PRINTF("b_lowlevel_status = 0x%x;\n", stmach->b_lowlevel_status);
    FIXED_PRINTF("b_application_status = 0x%x;\n", stmach->b_application_status);
    FIXED_PRINTF("b_bootloader_upgrade = 0x%x;\n", stmach->b_bootloader_upgrade);
    FIXED_PRINTF("b_lowlevel_upgrade = 0x%x;\n", stmach->b_lowlevel_upgrade);
    FIXED_PRINTF("b_application_upgrade = 0x%x;\n", stmach->b_application_upgrade);
    FIXED_PRINTF("b_bootloader_run_cnt = 0x%x;\n", stmach->b_bootloader_run_cnt);
    FIXED_PRINTF("b_lowlevel_run_cnt = 0x%x;\n", stmach->b_lowlevel_run_cnt);
    FIXED_PRINTF("b_application_run_cnt = 0x%x;\n", stmach->b_application_run_cnt);
    #if 0
    FIXED_PRINTF("b_need_upgrade = 0x%x;\n", stmach->b_need_upgrade);
    FIXED_PRINTF("b_backup_exist = 0x%x;\n", stmach->b_backup_exist);
    FIXED_PRINTF("b_lowlevel_backup_exist = 0x%x;\n", stmach->b_lowlevel_backup_exist);
    FIXED_PRINTF("b_boot_backup_exist = 0x%x;\n", stmach->b_boot_backup_exist);
    FIXED_PRINTF("b_nor_upgrade = 0x%x;\n", stmach->b_nor_upgrade);
    FIXED_PRINTF("b_nor_reserved = 0x%x;\n", stmach->b_nor_reserved);
    FIXED_PRINTF("b_nor_reserved_upgrade = 0x%x;\n", stmach->b_nor_reserved_upgrade);
    FIXED_PRINTF("b_nand_reserved = 0x%x;\n", stmach->b_nand_reserved);
    FIXED_PRINTF("b_nand_reserved_upgrade = 0x%x;\n", stmach->b_nand_reserved_upgrade);
    FIXED_PRINTF("b_nand_whole_upgrade = 0x%x;\n", stmach->b_nand_whole_upgrade);
    FIXED_PRINTF("\n");
    #endif
    FIXED_PRINTF("b_cur_uboot = 0x%x;\n", stmach->b_cur_uboot);

    FREE(buf_tmp);
    return RET_SUCCESS;
}

RET_CODE bl_save_stmach(struct state_machine_t *stmach)
{
    struct ali_nand_device *nand_dev;
    UINT32 bbsize, bbchecklen;
    unsigned long offset;
    
    if (stmach == NULL)
    {
        FIXED_PRINTF("stmach is null\n");
        return RET_FAILURE;
    }

    nand_dev = (struct ali_nand_device *)dev_get_by_name("ALI_NAND");
    if (nand_dev == NULL)
    {
        FIXED_PRINTF("nand_dev is null\n");
        return RET_FAILURE;
    }

    bbchecklen = 3*g_nand_info.blocksize;
    check_skip_len(nand_dev, (loff_t)(g_nand_info.stmach_addr - bbchecklen), bbchecklen, &bbsize);
    offset = g_nand_info.stmach_addr + bbsize;

    FIXED_PRINTF("save stmach:offset = 0x%x\n", offset);
    if (NF_SaveLoader(nand_dev, (loff_t)offset, (UINT8 *)stmach, sizeof(struct state_machine_t)) != 0)
    {
        FIXED_PRINTF("Save statemachine to nandflash fail\n");
        return RET_FAILURE;
    }

    return RET_SUCCESS;
}

RET_CODE bl_get_uboot_addr(UINT32 boot_type, UINT32 *uboot_addr)
{
    UINT32 nor_uboot_addr = 0, nand_uboot_addr = 0;
    struct state_machine_t stmach;
    struct ali_nand_device *nand_dev;

    if (uboot_addr == NULL)
    {
        FIXED_PRINTF("uboot_addr is null\n");
        return RET_FAILURE;
    }

    if (bl_init_nand() == 0)
    {
        memset((UINT8 *)&stmach, 0x0, sizeof(struct state_machine_t));
        if (bl_load_stmach(&stmach) != RET_SUCCESS)
        {
            FIXED_PRINTF("Load state machine failed\n");
            return RET_FAILURE;
        }

        if (stmach.b_bootloader_upgrade == UPG_DESC_BOOT_UPG_OVER)
        {
            if (stmach.b_cur_uboot == UPG_DESC_UBOOT2)
            {
                nor_uboot_addr = CHUNKID_MAINCODE;
                nand_uboot_addr = g_nand_info.uboot_start;
            }
            else
            {
                nor_uboot_addr = CHUNKID_UBOOT_BAK;
                nand_uboot_addr = g_nand_info.uboot_backup_start;  
            }
        }
        else
        {
            if (stmach.b_cur_uboot == UPG_DESC_UBOOT2)
            {
                nor_uboot_addr = CHUNKID_UBOOT_BAK;
                nand_uboot_addr = g_nand_info.uboot_backup_start;  
            }
            else
            {
                nor_uboot_addr = CHUNKID_MAINCODE;
                nand_uboot_addr = g_nand_info.uboot_start;
            }
        }

        if (stmach.b_bootloader_upgrade)
        {
            stmach.b_boot_status = UPG_DESC_BOOT_RUN_ENTER;
            
            if (stmach.b_bootloader_upgrade == UPG_DESC_BOOT_UPG_OVER)
            {//uboot upgrading complete,
                stmach.b_bootloader_upgrade = UPG_DESC_BOOT_UPG_RUN;
            }
            else if (stmach.b_bootloader_upgrade == UPG_DESC_BOOT_UPG_RUN)
            {// uboot upgrading complete, boot fail
                stmach.b_bootloader_upgrade = UPG_DESC_BOOT_UPG_NO;
            }

            if (bl_save_stmach(&stmach) != RET_SUCCESS)
            {
                FIXED_PRINTF("Load state machine failed\n");
                return RET_FAILURE;
            }
        }
    }
    else
    {//nand flash is empty
        nor_uboot_addr = CHUNKID_MAINCODE;
        nand_uboot_addr = g_nand_info.uboot_start;
    }

    if (boot_type != 1)
    {
        *uboot_addr = nor_uboot_addr;
    }
    else
    {
        *uboot_addr = nand_uboot_addr;
    }

    return RET_SUCCESS;
}
#endif
#endif

